// /////////////////////////////////////////////////////////////////////////////
//
// Name:            ColourAnalyser.cpp
// Author:          Michael Bartsch
//
// Desc :           This file contains the CColourAnalyser class. This class
//                  is responsible for analysing the colours in an image.
//
// License:         Copyright (C) 2008-2009 Michael Bartsch
//
//                  This program is free software: you can redistribute it
//                  and/or modify it under the terms of the GNU General Public
//                  License as published by the Free Software Foundation,
//                  either version 3 of the License, or (at your option) any
//                  later version.
//
//                  This program is distributed in the hope that it will be
//                  useful, but WITHOUT ANY WARRANTY; without even the implied
//                  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//                  PURPOSE. See the GNU General Public License for more
//                  details.
//
//                  You should have received a copy of the GNU General Public
//                  License along with this program. If not, see
//                  <http://www.gnu.org/licenses/>.
//
// /////////////////////////////////////////////////////////////////////////////

// Include required header file(s).
#include "ColourAnalyser.h"


// ColourAnalyser
// Constructor.
CColourAnalyser::CColourAnalyser()
: cameraImage(NULL), cameraBitmap(NULL), histogramR(NULL), histogramG(NULL),
  histogramB(NULL), isTargetColour(false), targetMargin(0), targetBorder(0),
  targetR(0), targetG(0), targetB(0)
{
#ifdef __OS_WINDOWS__
	// Initialise ESCAPI.
	// TODO: Replace with custom webcam capture library.
	devices = setupESCAPI();

	captureDevice.mWidth = 352;
	captureDevice.mHeight = 288;
	captureDevice.mTargetBuf = new int[352 * 288];

	connect(0);
#endif

    updateImage();
    analyseImage();
}

// Deconstructor.
CColourAnalyser::~CColourAnalyser()
{
}

// CColourAnalyser singleton.
CColourAnalyser* CColourAnalyser::pInstance = 0;

CColourAnalyser* CColourAnalyser::Instance()
{
	if(pInstance == 0)
	  pInstance = new CColourAnalyser;

	return pInstance;
}

// Loads the latest image from the webcam.
void CColourAnalyser::updateImage()
{
    // Check if an image has been loaded. If this is the case we delete it.
    if(cameraImage != NULL)
      delete cameraImage;

    if(cameraBitmap != NULL)
      delete cameraBitmap;

    // Retrieve image from webcam.
	cameraImage = new wxImage(352, 288, true);

#ifdef __OS_WINDOWS__
	if(devices != 0 && isConnected)
	{
	   doCapture(selectedDevice);

	   while (isCaptureDone(selectedDevice) == 0)
	   {
	   }

	   for(int x = 0; x < 352; x++)
	   {
	      for(int y = 0; y < 288; y++)
	      {
		     unsigned int r = (unsigned int)((captureDevice.mTargetBuf[x+y*352] >> 16) & 0x000000FF);
		     unsigned int g = (unsigned int)((captureDevice.mTargetBuf[x+y*352] >> 8) & 0x000000FF);
		     unsigned int b = (unsigned int)(captureDevice.mTargetBuf[x+y*352] & 0x000000FF);

		     cameraImage->SetRGB(x, y, r, g, b);
	      }
	   }
	}
#endif

    // Load image into the application.
    cameraBitmap = new wxBitmap(*cameraImage, -1);
}

// Analyses the last loaded image.
void CColourAnalyser::analyseImage()
{
    // Check if an image has been loaded.
    if(cameraImage == NULL)
      return;

    // Clear data from previous analysis's.
    memset(countR, 0, 256*sizeof(int));
    memset(countG, 0, 256*sizeof(int));
    memset(countB, 0, 256*sizeof(int));

    // Gather raw data.
    for(int y = 0; y < 288; y++)
    {
       for(int x = 0; x < 352; x++)
       {
           countR[cameraImage->GetRed(x, y)]++;
           countG[cameraImage->GetGreen(x, y)]++;
           countB[cameraImage->GetBlue(x, y)]++;
       }
    }

    // Get largest value.
    biggestValueR = biggestValueG = biggestValueB = 0;

    for(int i = 0; i < 256; i++)
    {
       if(countR[i] > biggestValueR)
       {
          biggestCountR = i;
          biggestValueR = countR[i];
       }
    }

    for(int i = 0; i < 256; i++)
    {
       if(countG[i] > biggestValueG)
       {
          biggestCountG = i;
          biggestValueG = countG[i];
       }
    }

    for(int i = 0; i < 256; i++)
    {
       if(countB[i] > biggestValueB)
       {
          biggestCountB = i;
          biggestValueB = countB[i];
       }
    }

    // Check if there is a match with the target (if set).
    isMatchRGB = false;

    if(isTargetColour)
    {
       // Red
       targetCountR = 0;
       isMatchR = false;

       for(int i = 0; i <= (targetMargin/2); i++)
       {
           if(targetR+i < 256)
             targetCountR += countR[targetR+i];

           if(targetR-i >= 0)
             targetCountR += countR[targetR-i];
       }

       if(targetCountR >= targetBorder)
         isMatchR = true;

       // Green
       targetCountG = 0;
       isMatchG = false;

       for(int i = 0; i <= (targetMargin/2); i++)
       {
           if(targetG+i < 256)
             targetCountG += countG[targetG+i];

           if(targetG-i >= 0)
             targetCountG += countG[targetG-i];
       }

       if(targetCountG >= targetBorder)
         isMatchG = true;

       // Blue
       targetCountB = 0;
       isMatchB = false;

       for(int i = 0; i <= (targetMargin/2); i++)
       {
           if(targetB+i < 256)
             targetCountB += countB[targetB+i];

           if(targetB-i >= 0)
             targetCountB += countB[targetB-i];
       }

       if(targetCountB >= targetBorder)
         isMatchB = true;

       // Is there a match?
       if(isMatchR && isMatchG && isMatchB)
         isMatchRGB = true;
    }

    // Generate graphical histograms from acquired data.
    if(histogramR != NULL)   delete histogramR;
    if(histogramG != NULL)   delete histogramG;
    if(histogramB != NULL)   delete histogramB;

    imageR = new wxImage("../media/histogramR.png", wxBITMAP_TYPE_PNG);
    imageG = new wxImage("../media/histogramG.png", wxBITMAP_TYPE_PNG);
    imageB = new wxImage("../media/histogramB.png", wxBITMAP_TYPE_PNG);

    for(int x = 0; x < 256; x++)
    {
       for(int y = 238-(countR[x]/biggestValueR)*237; y < 238; y++)
         imageR->SetRGB(x, y, 255, 0, 0);
    }

    for(int x = 0; x < 256; x++)
    {
       for(int y = 238-(countG[x]/biggestValueG)*237; y < 238; y++)
         imageG->SetRGB(x, y, 0, 255, 0);
    }

    for(int x = 0; x < 256; x++)
    {
       for(int y = 238-(countB[x]/biggestValueB)*237; y < 238; y++)
         imageB->SetRGB(x, y, 0, 0, 255);
    }

    histogramR = new wxBitmap(*imageR, -1);
    histogramG = new wxBitmap(*imageG, -1);
    histogramB = new wxBitmap(*imageB, -1);

    delete imageR;
    delete imageG;
    delete imageB;
}

void CColourAnalyser::connect(int ID)
{
#ifdef __OS_WINDOWS__
	if(devices != 0)
	{
	   // Deinit previous device.
	   if(isConnected)
		 deinitCapture(selectedDevice);

	   // Init new device.
	   isConnected = initCapture(ID, &captureDevice);
	   selectedDevice = ID;
	}
#endif
}

// Get Functions - [BEGIN]
wxImage* CColourAnalyser::getCameraImage()
{
    return cameraImage;
}

wxBitmap* CColourAnalyser::getCameraBitmap()
{
    return cameraBitmap;
}

wxBitmap* CColourAnalyser::getHistogramR()
{
    return histogramR;
}

wxBitmap* CColourAnalyser::getHistogramG()
{
    return histogramG;
}

wxBitmap* CColourAnalyser::getHistogramB()
{
    return histogramB;
}

bool CColourAnalyser::getIsTarget()
{
    return isTargetColour;
}

bool CColourAnalyser::getIsMatchRGB()
{
    return isMatchRGB;
}

bool CColourAnalyser::getIsMatchR()
{
    return isMatchR;
}

bool CColourAnalyser::getIsMatchG()
{
    return isMatchG;
}

bool CColourAnalyser::getIsMatchB()
{
    return isMatchB;
}

int CColourAnalyser::getTargetR()
{
    return targetR;
}

int CColourAnalyser::getTargetG()
{
    return targetG;
}

int CColourAnalyser::getTargetB()
{
    return targetB;
}

int CColourAnalyser::getTargetMargin()
{
    return targetMargin;
}

int CColourAnalyser::getTargetBorder()
{
    return targetBorder;
}

int CColourAnalyser::getBiggestR()
{
    return biggestValueR;
}

int CColourAnalyser::getBiggestG()
{
    return biggestValueG;
}

int CColourAnalyser::getBiggestB()
{
    return biggestValueB;
}

int CColourAnalyser::getSelectedDevice()
{
#ifdef __OS_WINDOWS__
    return selectedDevice;
#elif __OS_LINUX__
    return 0;
#endif
}

// Returns the number of available devices.
int CColourAnalyser::getDevices()
{
#ifdef __OS_WINDOWS__
    return devices;
#elif __OS_LINUX__
    return 0;
#endif
}
// Get Functions - [END]

// Set Functions - [BEGIN]
void CColourAnalyser::setIsTarget(bool value)
{
    isTargetColour = value;
}

void CColourAnalyser::setTargetR(int value)
{
    targetR = value;
}

void CColourAnalyser::setTargetG(int value)
{
    targetG = value;
}

void CColourAnalyser::setTargetB(int value)
{
    targetB = value;
}

void CColourAnalyser::setTargetMargin(int value)
{
    targetMargin = value;
}

void CColourAnalyser::setTargetBorder(int value)
{
    targetBorder = value;
}
// Set Functions - [END]
// End of File
